package snapex.core.security;

import java.security.Principal;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.common.util.RandomValueStringGenerator;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AccessTokenConverter;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Configuration
@RestController
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
	
//	@Value("${snapex.security.accessTokenValiditySeconds}")
//    private int accessTokenValiditySeconds;
//	
//	@Value("${snapex.security.refreshTokenValiditySeconds}")
//    private int refreshTokenValiditySeconds;
	
	@Autowired
	private AuthenticationManager authenticationManager;
	
	@Autowired
	private TokenStore tokenStore;
	
	@Autowired
	private AccessTokenConverter accessTokenConverter;
	
	@Autowired
	private TokenEnhancer tokenEnhancer;
	
//	@Autowired
//	private ClientDetailsService clientDetailsService;
	
    @Autowired
    private DataSource dataSource;
    
    @Bean	
    public ClientDetailsService clientDetailsService() {  
    	log.debug("created clientDetailsService*********");
    	
    	JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(this.dataSource);
    	
    	//clientDetailsService.setPasswordEncoder(new BCryptPasswordEncoder());
    	
    	return clientDetailsService;
    }
    

	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		clients.withClientDetails(this.clientDetailsService());	
	}
	
	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {		
		endpoints.authenticationManager(this.authenticationManager);
		
		endpoints.tokenStore(this.tokenStore);
		endpoints.accessTokenConverter(this.accessTokenConverter);
		endpoints.tokenEnhancer(tokenEnhancer);
		
		endpoints.tokenServices(defaultTokenServices());
		
		endpoints.authorizationCodeServices(new MyInMemoryAuthorizationCodeServices());
		
//		endpoints.allowedTokenEndpointRequestMethods(HttpMethod.POST, HttpMethod.GET);
	}
	
	@Primary
	@Bean
	public DefaultTokenServices defaultTokenServices() {
		DefaultTokenServices tokenServices = new DefaultTokenServices();
		tokenServices.setTokenStore(tokenStore);
		tokenServices.setTokenEnhancer(this.tokenEnhancer);
		tokenServices.setSupportRefreshToken(true);
		tokenServices.setClientDetailsService(this.clientDetailsService());
//		tokenServices.setAccessTokenValiditySeconds(accessTokenValiditySeconds); 
//		tokenServices.setRefreshTokenValiditySeconds(refreshTokenValiditySeconds);
		
		return tokenServices;
	}

	@Override
	public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {		
		oauthServer.checkTokenAccess("isAuthenticated()");
	}
	
    @RequestMapping("/user")
    public Principal user(Principal user) {
    	log.debug("******processing request '/user' (username:{})", user.getName());
    	
        return user;
    }
   
    class MyInMemoryAuthorizationCodeServices extends InMemoryAuthorizationCodeServices {
    	private RandomValueStringGenerator generator = new RandomValueStringGenerator(32);
    	
		@Override
		public String createAuthorizationCode(OAuth2Authentication authentication) {
			
			String code = generator.generate();
			store(code, authentication);
			
			log.debug("******Generate authorization code[{}] for principal[{}]", code, authentication.getName());
							
			return code;
		}        	
    }
}
